using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using UnityEditor;
using System;
using System.Reflection;

public abstract class AbstractMap
{
    public const float BLOCK_SIZE = 1f;

    public static System.Random Random;

    private HashSet<Slot> workArea = new HashSet<Slot>();
    public readonly Queue<Slot> BuildQueue = new ();

    public readonly short[][] InitialModuleHealth;

    public AbstractMap()
    {
    }

    public abstract Slot GetSlot(Vector3Int position);

    public abstract IEnumerable<Slot> GetAllSlots();

    public void NotifySlotCollapsed(Slot slot)
    {
        if (this.workArea != null)
        {
            this.workArea.Remove(slot);
        }
        this.BuildQueue.Enqueue(slot);
    }

    public void NotifySlotCollapseUndone(Slot slot)
    {
        if (this.workArea != null)
        {
            this.workArea.Add(slot);
        }
    }

    public void FinishRemovalQueue()
    {
    }

    public void EnforceWalkway(Vector3Int start, int direction)
    {
        var slot = this.GetSlot(start);
    }

    public void EnforceWalkway(Vector3Int start, Vector3Int destination)
    {
        int direction = Orientations.GetIndex((Vector3)(destination - start));
        this.EnforceWalkway(start, direction);
        this.EnforceWalkway(destination, (direction + 3) % 6);
    }

    public void Collapse(IEnumerable<Vector3Int> targets, bool showProgress = false)
    {
#if UNITY_EDITOR
        try
        {
#endif
            this.workArea = new HashSet<Slot>(targets.Select(target => this.GetSlot(target)).Where(slot => slot != null && !slot.Collapsed));

            while (this.workArea.Any())
            {
                float minEntropy = float.PositiveInfinity;
                Slot selected = null;

                foreach (var slot in workArea)
                {
                    selected = slot;
                    break;
                }
                selected.CollapseRandom();

#if UNITY_EDITOR
                if (showProgress && this.workArea.Count % 20 == 0)
                {
                    if (EditorUtility.DisplayCancelableProgressBar("Collapsing area... ", this.workArea.Count + " left...", 1f - (float)this.workArea.Count() / targets.Count()))
                    {
                        EditorUtility.ClearProgressBar();
                        throw new Exception("Map generation cancelled.");
                    }
                }
#endif
            }

#if UNITY_EDITOR
            if (showProgress)
            {
                EditorUtility.ClearProgressBar();
            }
        }
        catch (Exception exception)
        {
            if (showProgress)
            {
                EditorUtility.ClearProgressBar();
            }
            Debug.LogWarning("Exception in world generation thread at" + exception.StackTrace);
            throw exception;
        }
#endif
    }

    public void Collapse(Vector3Int start, Vector3Int size, bool showProgress = false)
    {
        var targets = new List<Vector3Int>();
        for (int x = 0; x < size.x; x++)
        {
            for (int y = 0; y < size.y; y++)
            {
                for (int z = 0; z < size.z; z++)
                {
                    targets.Add(start + new Vector3Int(x, y, z));
                }
            }
        }
        this.Collapse(targets, showProgress);
    }

    public void Undo(int steps)
    {
    }

    public short[][] CopyInititalModuleHealth()
    {
        return this.InitialModuleHealth.Select(a => a.ToArray()).ToArray();
    }
}
